import {
    addIcon, App, HeadingCache, MarkdownView, Modal, Notice,
    Plugin, PluginSettingTab, Setting, TFile, TFolder, Vault
} from "obsidian";
import * as Path from "path";
// import {getFileByLink} from "./util";
import {Common} from "./common";
import {getLinksWithoutMetadata} from "./links";
import {concat} from "./contentConcat";
import {AdvancedTextLocator} from "./positionFull";

export interface LinkSectionInfo {
    hasSection: boolean
    link: string
    section: string
}

interface PluginSettings {
    vaultReplaceStartLocation: string,
}

const DEFAULT_SETTINGS: PluginSettings = {
    vaultReplaceStartLocation: "0_Workshop/working/obsidian-notes-chain/note-chain-1.md",
}

//Add chevron-up-square icon from lucide for mobile toolbar (temporary until Obsidian updates to Lucide v0.130.0)
addIcon("chevron-up-square", `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up-square"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect><polyline points="8,14 12,10 16,14"></polyline></svg>`);

export default class NotesChainPlugin extends Plugin {
    settings: PluginSettings

    async onload() {
        await this.loadSettings();

        // this.addCommand({
        //     id: "generate-notes-chain",
        //     name: "Generate Notes Chain (Vault)",
        //     icon: "plus-square",
        //     callback: async () => {
        //         await this.generateNotesChain();
        //     },
        // });

        this.addCommand({
            id: "test-notes-chain",
            name: "Aggregate Notes Chain (Test)",
            icon: "plus-square",
            callback: async () => {
                await this.test();
            },
        });

        // this.addCommand({
        //     id: "aggregate-notes-chain",
        //     name: "Aggregate Notes Chain (Vault)",
        //     icon: "plus-square",
        //     callback: async () => {
        //         await this.aggregateNotesChain();
        //     },
        // });
        //
        // this.addCommand({
        //     id: "generate-aggregate-notes-chain-vault",
        //     name: "Generate & Aggregate Notes Chain (Vault)",
        //     icon: "plus-square",
        //     callback: async () => {
        //         await this.generateNotesChain();
        //         await this.aggregateNotesChain();
        //     },
        // });

        this.addCommand({
            id: "generate-aggregate-notes-chain-page",
            name: "Generate & Aggregate Notes Chain (Page)",
            icon: "plus-square",
            callback: async () => {
                await this.generateAndAggregateNotesChainByCurrentPage()
            },
        });

        this.addCommand({
            id: "generate-ref-links-page",
            name: "Generate Ref Links (Page)",
            icon: "plus-square",
            callback: async () => {
                await this.generateRefNotesLink(this.app.workspace.getActiveFile());
            },
        });

        this.addCommand({
            id: "generate-reversed-ref-links-page",
            name: "Generate Reversed Ref Links (Page)",
            icon: "plus-square",
            callback: async () => {
                await this.generateReversedRefNotesLink(this.app.workspace.getActiveFile());
            },
        });
        // add settings tab
        this.addSettingTab(new SettingTab(this.app, this));
    }

    async loadSettings() {
        this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
    }

    async saveSettings() {
        await this.saveData(this.settings);
    }

    async test() {
        // 1. 获取 Dataview 插件实例
        //@ts-ignore
//         const dataviewPlugin = this.app.plugins.plugins.dataview;
//         if (!dataviewPlugin || !dataviewPlugin.api) {
//             new Notice('❌ 请先安装并启用 Dataview 插件。');
//             return;
//         }
//         // 2. 获取 API
//         const dv = dataviewPlugin.api;
//
//         // 3. 获取当前页面数据
//         const currentPage = dv.current();
//         if (!currentPage) {
//             new Notice('⚠️ 无法获取当前页面信息。');
//             return;
//         }
//         // 4. 读取内联字段 "[跑步::5km]"
//         const runningData = currentPage['跑步']; // 或 currentPage.跑步（如果字段名是有效的 JS 标识符）
// // 读取名为 "跑步" 的内联字段值
//         console.log(runningData); // 输出: 5km
        const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);

        if (!mdView) return false;
        if (mdView.editor == undefined) return false;
        const fileCachedMetadata = this.app.metadataCache.getFileCache(mdView.file);
        console.log(fileCachedMetadata);
        const noteContent = `# 项目计划

## 第一阶段
- 完成需求分析 (优先级: 高)
- 设计系统架构 (优先级: 中)
- 编写技术文档 (优先级: 低)

## 第二阶段
- 开发核心功能 (优先级: 高)
- 进行单元测试 (优先级: 中)`;

        console.log('\n=== 示例2: 正则表达式匹配 ===');
        const regexResults = AdvancedTextLocator.locateText(
            noteContent,
            '- 完成.*\\n-.*\\)',
            { isRegex: true, includeCaptureGroups: true }
        );
        console.log(regexResults);

//         const targetText = `## 第一阶段
// - 完成需求分析
// - 设计系统架构`;
//
//         const positions = locateMultiLineText(noteContent, targetText);
//         console.log(positions);
    }

    async generateAndAggregateNotesChainByCurrentPage() {
        const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);

        if (!mdView) return false;
        if (mdView.editor == undefined) return false;
        await this.insertPrevNextLinksInFile(mdView.file);
        await this.aggregatePrevNextLinksInFile(mdView.file);
    }

    async aggregateNotesChain() {
        const files: TFile[] = [];
        const startLocation = this.settings.vaultReplaceStartLocation.trim();
        const folderOrFile = this.app.vault.getAbstractFileByPath(startLocation);
        if (folderOrFile instanceof TFile) {
            files.push(folderOrFile);
        } else if (folderOrFile instanceof TFolder) {
            Vault.recurseChildren(folderOrFile, (file => {
                if (file instanceof TFile && file.extension === "md") {
                    files.push(file);
                }
            }));
        }

        for (const file of files) {
            await this.aggregatePrevNextLinksInFile(file);
        }
    }

    async generateNotesChain() {
        const files: TFile[] = [];
        const startLocation = this.settings.vaultReplaceStartLocation.trim();
        const folderOrFile = this.app.vault.getAbstractFileByPath(startLocation);
        if (folderOrFile instanceof TFile) {
            files.push(folderOrFile);
        } else if (folderOrFile instanceof TFolder) {
            Vault.recurseChildren(folderOrFile, (file => {
                if (file instanceof TFile && file.extension === "md") {
                    files.push(file);
                }
            }));
        }

        for (const file of files) {
            await this.insertPrevNextLinksInFile(file);
        }
    }

    async insertPrevNextLinksInFile(file: TFile) {
        const parsedLinks = await getLinksWithoutMetadata(this.app, file);
        console.log(parsedLinks);
        const fileContents = await this.app.vault.read(file);
        // const cacheItem = this.app.metadataCache.getCache(file.path);

        // if (!cacheItem.links)
        //     return;
        for (const link of parsedLinks) {
            const linkedFile = this.getFileByLink(link.target, file.path);
            // console.log(linkedFile.path);

            // const linkLine = mdView.editor.getValue().split("\n")[linkLineNo]

            const linkLine = fileContents.split("\n")[link.lineNumber];
            console.log(linkLine);
            if (linkLine.startsWith("Prev")) {
                // 如果这个link的前缀是Prev，打开目标文件，构建Next链接
                let contents = await this.app.vault.read(linkedFile as TFile);

                const fileCachedMetadata = this.app.metadataCache.getFileCache(linkedFile)
                const insertText = `Next: [[${file.basename}]]`;
                let startOffset = 0;
                if (fileCachedMetadata && fileCachedMetadata.frontmatter) {
                    startOffset = fileCachedMetadata.frontmatterPosition.end.offset;
                }
                if (startOffset == 0) {
                    contents = concat(contents, startOffset, insertText, 0, 1);
                } else {
                    contents = concat(contents, startOffset, insertText);
                }
                // if (!contents.contains(insertText)) {
                //     let emptyLine = "\n";
                //     if (startOffset == 0) {
                //         emptyLine = "";
                //     }
                //     contents = contents.slice(0, startOffset).trimEnd() + `${emptyLine}${insertText}\n` + contents.slice(startOffset).trimStart();
                // }
                // if (!contents.contains(insertText)) {
                //     contents = contents + "\n" + insertText;
                // }
                await this.app.vault.modify(linkedFile, contents);
                // this.app.metadataCache.trigger("changed", linkedFile);
            } else if (linkLine.startsWith("Next")) {
                // 如果这个link的前缀是Next，打开目标文件，构建Prev链接
                let contents = await this.app.vault.read(linkedFile as TFile);

                const fileCachedMetadata = this.app.metadataCache.getFileCache(linkedFile)
                const insertText = `Prev: [[${file.basename}]]`;
                let startOffset = 0;
                if (fileCachedMetadata && fileCachedMetadata.frontmatter) {
                    startOffset = fileCachedMetadata.frontmatterPosition.end.offset;
                }
                if (startOffset == 0) {
                    contents = concat(contents, startOffset, insertText, 0, 1);
                } else {
                    contents = concat(contents, startOffset, insertText);
                }
                // if (!contents.contains(insertText)) {
                //     let emptyLine = "\n";
                //     if (startOffset == 0) {
                //         emptyLine = "";
                //     }
                //     contents = contents.slice(0, startOffset).trimEnd() + `${emptyLine}${insertText}\n` + contents.slice(startOffset).trimStart();
                // }
                await this.app.vault.modify(linkedFile, contents);
                // this.app.metadataCache.trigger("changed", linkedFile);
            }
            // else {
            //     // 如果都不是，打开目标文件，构建“相关链接”block
            //     let contents = await this.app.vault.read(file as TFile);
            //     const insertText = `相关链接: [[${mdView.file.basename}]]`;
            //     if (!contents.contains(insertText)) {
            //         contents = contents + "\n" + insertText;
            //     }
            //     await this.app.vault.modify(file, contents);
            // }
        }
    }

    async aggregatePrevNextLinksInFile(file: TFile) {
        // 找到一篇笔记中所有的"Prev:"，将其聚合成一行，便于阅读
        // const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);
        //
        // if (!mdView) return false;
        // if (mdView.editor == undefined) return false;
        //
        // const lines = mdView.editor.getValue().split("\n");
        const fileContents = await this.app.vault.read(file);
        const lines = fileContents.split("\n");
        let prevLinkList: string[] = [];
        let nextLinkList: string[] = [];
        const newLines = []
        for (const line of lines) {
            if (line.trimStart().startsWith("Prev:")) {
                const links = line.split(",").map(line => {return line.replace("Prev:", "").trim();});
                prevLinkList = prevLinkList.concat(links);
            } else if (line.trimStart().startsWith("Next:")) {
                const links = line.split(",").map(line => {return line.replace("Next:", "").trim();});
                nextLinkList = nextLinkList.concat(links);
            } else {
                newLines.push(line);
            }
        }
        // newLines.unshift("Prev: " + prevLinkList.join(", "));
        // newLines.push("Next: " + nextLinkList.join(", "));
        let contents = newLines.join("\n");

        if (nextLinkList.length > 0) {
            nextLinkList = [...new Set(nextLinkList)];
            let startOffset = 0;

            const fileCachedMetadata = this.app.metadataCache.getFileCache(file)
            if (fileCachedMetadata && fileCachedMetadata.frontmatter) {
                startOffset = fileCachedMetadata.frontmatterPosition.end.offset;
            }

            const insertText = "Next: " + nextLinkList.join(", ").trim();
            if (startOffset == 0) {
                contents = concat(contents, startOffset, insertText, 0, 1);
            } else {
                contents = concat(contents, startOffset, insertText);
            }
            // if (!contents.contains(insertText)) {
            //     let emptyLine = "\n";
            //     if (startOffset == 0) {
            //         emptyLine = "";
            //     }
            //     contents = contents.slice(0, startOffset).trimEnd() + `${emptyLine}${insertText}\n` + contents.slice(startOffset).trimStart();
            // }
        }
        if (prevLinkList.length > 0) {
            prevLinkList = [...new Set(prevLinkList)];
            let startOffset = 0;

            const fileCachedMetadata = this.app.metadataCache.getFileCache(file)
            if (fileCachedMetadata && fileCachedMetadata.frontmatter) {
                startOffset = fileCachedMetadata.frontmatterPosition.end.offset;
            }

            const insertText = "Prev: " + prevLinkList.join(", ").trim();
            if (startOffset == 0) {
                contents = concat(contents, startOffset, insertText, 0, 1);
            } else {
                contents = concat(contents, startOffset, insertText);
            }
            // if (!contents.contains(insertText)) {
            //     let emptyLine = "\n";
            //     if (startOffset == 0) {
            //         emptyLine = "";
            //     }
            //     contents = contents.slice(0, startOffset).trimEnd() + `${emptyLine}${insertText}\n` + contents.slice(startOffset).trimStart();
            // }
        }

        // mdView.editor.setValue(contents);
        await this.app.vault.modify(file, contents);
        // this.app.metadataCache.trigger("changed", file);
        // const resolveListener = this.app.metadataCache.on(
        //     'resolve',
        //     (resolvedFile) => {
        //         // 检查触发事件的文件是否是我们关心的目标文件
        //         if (resolvedFile.path === file.path) {
        //             // 一旦确认，执行你的后续逻辑
        //             console.log('元数据已更新完成！', this.app.metadataCache.getFileCache(file));
        //
        //             // 重要：执行完操作后，注销该事件监听器，避免内存泄漏
        //             // this.app.metadataCache.off('resolve', resolveListener);
        //         }
        //     }
        // );
    }

    async generateReversedRefNotesLink(file: TFile) {
        // 在你的插件代码中
        // let currentFile = this.app.workspace.getActiveFile(); // 获取当前活动文件
        let currentFile = file;
        let allResolvedLinks = this.app.metadataCache.resolvedLinks;
        let backlinks = [];

        // 遍历所有文件的已解析链接
        for (let sourcePath in allResolvedLinks) {
            let targetFiles = allResolvedLinks[sourcePath];
            if (currentFile.path in targetFiles) {
                // 找到包含当前笔记链接的源文件
                let sourceFile = this.app.vault.getAbstractFileByPath(sourcePath);
                if (sourceFile instanceof TFile) {
                    backlinks.push(sourceFile);
                }
            }
        }

        // 现在 backlinks 数组中就包含了所有反向链接的 TFile 对象
        console.log("反向链接: ", backlinks.map(file => file.path));

        const fileContents = await this.app.vault.read(file);
        const regexResults = AdvancedTextLocator.locateText(
            fileContents,
            '反向链接:\\s*\\n(?:\\s*\\*[^\\n]*)+\\s?', //相关(引用|笔记)(：|:)?\s*\n(?:\s*\*[^\n]*)+\s?
            { isRegex: true, includeCaptureGroups: true }
        );
        const backlinksText = backlinks.map(file => "[[" + file.basename + "]]");
        const insertText = "* " + backlinksText.join("\n* ").trim();
        if (regexResults.length > 0) {
            const endPosition = regexResults[0].end.offset + 1;
            let contents;
            if (regexResults[0].matchedText.endsWith("\n")) {
                contents = concat(fileContents, endPosition, insertText, 0, 1);
            } else {
                contents = concat(fileContents, endPosition, insertText, 1, 0);
            }

            await this.app.vault.modify(file, contents);
        } else {
            const contents = fileContents + "\n反向链接:\n" + insertText;
            await this.app.vault.modify(file, contents);
        }
    }

    async generateRefNotesLink(file: TFile) {
        const parsedLinks = await getLinksWithoutMetadata(this.app, file);
        console.log(parsedLinks);
        const fileContents = await this.app.vault.read(file);
        const linkList = [];

        for (const link of parsedLinks) {
            const linkLine = fileContents.split("\n")[link.lineNumber];
            console.log(linkLine);
            if (!linkLine.startsWith("Prev") && !linkLine.startsWith("Next")) {
                linkList.push(link.rawText)
            }
        }

        const insertText = "* " + linkList.join("\n* ").trim();
        const regexResults = AdvancedTextLocator.locateText(
            fileContents,
            '相关笔记:\\s*\\n(?:\\s*\\*[^\\n]*)+\\s?', //相关(引用|笔记)(：|:)?\s*\n(?:\s*\*[^\n]*)+\s?
            { isRegex: true, includeCaptureGroups: true }
        );
        if (regexResults.length > 0) {
            const endPosition = regexResults[0].end.offset + 1;
            let contents;
            if (regexResults[0].matchedText.endsWith("\n")) {
                contents = concat(fileContents, endPosition, insertText, 0, 1);
            } else {
                contents = concat(fileContents, endPosition, insertText, 1, 0);
            }

            await this.app.vault.modify(file, contents);
        } else {
            const contents = fileContents + "\n相关笔记:\n" + insertText;
            await this.app.vault.modify(file, contents);
        }
    }
    // async generateRefNotesLink() {
    //     // 找到一篇笔记中所有除了"Prev:"和"Next:"的所有笔记链接，将其聚合成"相关链接:xxx"
    //     const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);
    //
    //     if (!mdView) return false;
    //     if (mdView.editor == undefined) return false;
    //
    //     const cacheItem = this.app.metadataCache.getCache(mdView.file.path);
    //     console.log(cacheItem.links);
    //     for (const link of cacheItem.links) {
    //         const file = this.getFileByLink(link.link, mdView.file.path);
    //         console.log(file.path);
    //         // const linkStartOffset = link.position.start.offset
    //         // TODO: 这里-6个就不够了，如果是聚合模式，应该找到这一样看起头字符串
    //         // const linkPrefix = mdView.editor.getValue().slice(linkStartOffset - 6, linkStartOffset).trimEnd();
    //
    //         const linkLineNo = link.position.start.line;
    //         const linkLine = mdView.editor.getValue().split("\n")[linkLineNo]
    //         console.log(linkLine);
    //         if (!linkLine.startsWith("Prev") && !linkLine.startsWith("Next")) {
    //             // 如果都不是，打开目标文件，构建“相关链接”block
    //             let contents = await this.app.vault.read(file as TFile);
    //             const insertText = `相关链接: [[${mdView.file.basename}]]`;
    //             if (!contents.contains(insertText)) {
    //                 contents = contents + "\n" + insertText;
    //             }
    //             await this.app.vault.modify(file, contents);
    //         }
    //     }
    //
    // }

    getFileByLink(link: string, owningNotePath: string, allowInvalidLink: boolean = true): TFile | undefined {
        if (link.trim() === ""){ // JimmyMo process link with only hash e.g. [[#^i0xwp8]]
            return undefined;
        }
        link = this.splitLinkToPathAndSection(link).link;
        if (allowInvalidLink) {
            return this.app.metadataCache.getFirstLinkpathDest(link, owningNotePath);
        }
        let fullPath = this.getFullPathForLink(link, owningNotePath);
        return this.getFileByPath(fullPath);
    }

    splitLinkToPathAndSection(link: string): LinkSectionInfo {
        let res: LinkSectionInfo = {
            hasSection: false,
            link: link,
            section: ""
        }

        if (!link.contains('#'))
            return res;

        //@ts-ignore
        let linkBeforeHash = link.match(/(.*?)#(.*?)$/)[1];
        //@ts-ignore
        let section = link.match(/(.*?)#(.*?)$/)[2];

        // JimmyMo to compatiable to image hash like xxx.png#inline|300
        let isMarkdownSection = section != ""; // && linkBeforeHash.endsWith(".md"); // for links with sections like [](note.md#section)
        let isPdfPageSection = section.startsWith("page=") && linkBeforeHash.endsWith(".pdf"); // for links with sections like [](note.pdf#page=42)

        if (isMarkdownSection || isPdfPageSection) {
            res = {
                hasSection: true,
                link: linkBeforeHash,
                section: section
            }
        }

        return res;
    }

    getFullPathForLink(link: string, owningNotePath: string): string {
        link = this.splitLinkToPathAndSection(link).link;
        link = Common.normalizePathForFile(link);
        owningNotePath = Common.normalizePathForFile(owningNotePath);

        let parentFolder = owningNotePath.substring(0, owningNotePath.lastIndexOf("/"));
        let fullPath = Path.join(parentFolder, link);

        fullPath = Common.normalizePathForFile(fullPath);
        return fullPath;
    }
    getFileByPath(path: string): TFile {
        path = Common.normalizePathForFile(path);
        return this.app.vault.getAbstractFileByPath(path) as TFile;
    }
    getMarkdownFiles(startLocation: string) : TFile[] {
        const files: TFile[] = [];
        const folderOrFile = this.app.vault.getAbstractFileByPath(startLocation);
        // const folderOrFile = this.app.vault.getAbstractFileByPath("1_Inbox/Diary/2023-07-28.md");
        if (folderOrFile instanceof TFile) {
            files.push(folderOrFile);
        } else if (folderOrFile instanceof TFolder) {
            Vault.recurseChildren(folderOrFile, (file => {
                if (file instanceof TFile && file.extension === "md") {
                    files.push(file);
                }
            }));
        }
        files.sort((a, b)=>{
            if (a.basename.toLowerCase() > b.basename.toLowerCase()) return -1;
            if (a.basename.toLowerCase() < b.basename.toLowerCase()) return 1;
            return 0;
        });
        return files;
    }

    async writeToOutputPath(outputPath: string, headingText: string, content: string, openFile: boolean = true) {
        let outputFilePath: string;
        if (outputPath.endsWith(".md")) {
            outputFilePath = outputPath;
        } else {
            outputFilePath = `${outputPath}/${headingText}.md`;
        }
        if (!await this.app.vault.adapter.exists(Path.dirname(outputFilePath))) {
            await this.app.vault.createFolder(Path.dirname(outputFilePath));
        }

        await this.writeAndOpenFile(
            this.app,
            outputFilePath,
            content,
            openFile
        );
    }

    async writeAndOpenFile(app: App, outputFileName: string, text: string, openFile: boolean) {
        await app.vault.adapter.write(outputFileName, text);
        if (!openFile) return;

        let fileIsAlreadyOpened = false;
        app.workspace.iterateAllLeaves((leaf) => {
            if (
                leaf.getDisplayText() != "" &&
                outputFileName.startsWith(leaf.getDisplayText())
            ) {
                fileIsAlreadyOpened = true;
            }
        });
        if (!fileIsAlreadyOpened) {
            const newPane = app.workspace.getLeavesOfType("empty").length == 0;
            if (newPane) {
                app.workspace.openLinkText(outputFileName, "/", true);
            } else {
                const file = app.vault.getAbstractFileByPath(outputFileName);

                if (file instanceof TFile) {
                    await app.workspace
                        .getLeavesOfType("empty")[0]
                        .openFile(file);
                } else {
                    app.workspace.openLinkText(outputFileName, "/", true);
                }
            }
        }
    }
}

class SettingTab extends PluginSettingTab {
    plugin: NotesChainPlugin;

    constructor(app: App, plugin: NotesChainPlugin) {
        super(app, plugin);
        this.plugin = plugin;
    }

    arraymove<T>(
        arr: T[],
        fromIndex: number,
        toIndex: number
    ): void {
        if (toIndex < 0 || toIndex === arr.length) {
            return;
        }
        const element = arr[fromIndex];
        arr[fromIndex] = arr[toIndex];
        arr[toIndex] = element;
    }

    display(): void {
        const {containerEl} = this;
        containerEl.empty();

        /* Single Jobs Setting */
        this.containerEl.createEl("h2", { text: "Define Single Jobs" });
        const desc = document.createDocumentFragment();
        desc.append(
            "Define configuration for this plugin."
        );
        new Setting(this.containerEl).setDesc(desc);

        new Setting(this.containerEl)
            .setName('Vault Start Location')
            .setDesc('the root location for vault level.')
            .addText(text => text
                .setPlaceholder("e.g. 0_Workshop/working/obsidian-notes-chain")
                .setValue(this.plugin.settings.vaultReplaceStartLocation)
                .onChange(async (value) => {
                        this.plugin.settings.vaultReplaceStartLocation = value;
                        await this.plugin.saveSettings();
                    }
                ));
    }
}
